/* 
 * File:   main.cpp
 * Author: Dwarf
 *
 * Created on 25. září 2011, 10:11
 */

#include <stdlib.h>
#include <cstdlib>
#include <iostream>
#include <stack>
#include <vector>
#include "structs.h"
#include "Game.h"
#include <time.h>
#include <fstream>
#include <string.h>
#include <sstream>


//#define DEBUG 1

using namespace std;
int n = 0, s = 0, r = 0 ;
int my_rank = 0;
int p = 0;
Game* game;
ofstream log;
double t1,t2;

//vysledne reseni
vector<Move> solution;

int init[3];
int* board;

int initStarter(int &argc, char** &argv) {
    if (argc < 5) {
        cout << "Spatne vstupni parametry" << endl;
        exit(0);
    } else {
        init[0] = n = atoi(argv[1]);
        init[1] = s = atoi(argv[2]);
        init[2] = r = atoi(argv[3]);

//        cout << n << " " << s << " " << r << endl;
        game = new Game(n, s, r);

        if (atoi(argv[4]) == 1) {
//            cout << "FILE " << argv[5] << endl;
            ifstream is(argv[5]);
            char znak;
            string slovo = "";
            int tower = 1, disc;
            board = new int[n];
            char *buf;

            if (is.fail()) return -1;
            while (true) {
                is.read(&znak, 1);

                //                if ( is.eof() ) break;
                if ((isspace(znak) || is.eof()) && slovo != "") {
                    //                    disc = atoi(&slovo.c_str());
                    buf = new char[slovo.size()];
                    for (int i = 0; i < slovo.size(); i++) {
                        buf[i] = slovo[i];
                    }
//                    cout << "buf " << *buf << endl;
                    disc = atoi(buf);
//                    cout << "disc " << disc << endl;
                    if (disc > n) {
                        cout << "Ve vstupnim souboru je vetsi disk nez zadany pocet" << endl;
                        return 0;
                    }
                    board[disc - 1] = tower;
                    slovo = "";
                } else if (!isspace(znak)) slovo += znak;
                if (znak == '\n') tower++;
                if (is.eof()) break;

            }
            game->newGame(board);
        } else {
            game->newGame();
            board = game->getDiscs();
        }
    }




//    for (int i = 0; i < n; i++) {
//        cout << board[i] << " ";
//    }
//    cout << endl;
    
    MPI_Barrier(MPI_COMM_WORLD);
    t1 = MPI_Wtime();
    for (int i = 1; i < p; i++) {
#ifdef DEBUG
        log << "SENDING INIT TO " << i << endl;
#endif
        MPI_Send(init, 3, MPI_INT, i, MSG_INIT, MPI_COMM_WORLD);
#ifdef DEBUG
        log << "INIT SENT TO " << i << endl;
        log << "SENDING BOARD TO " << i << endl;
        for (int j = 0; j < init[0]; j++) {
            log << board[j] << " ";
        }
        log << endl;
#endif
        MPI_Send(board, n, MPI_INT, i, MSG_INIT_DATA, MPI_COMM_WORLD);
#ifdef DEBUG
        log << "BOARD SENT TO " << i << endl;
#endif
    }

#ifdef DEBUG
//    game->printGame(log);
#endif
}

int main(int argc, char** argv) {   
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &p);
    int pom[3] = {0,0,0};
    MPI_Status status;

#ifdef DEBUG    
    char logname[10];    
    ostringstream oss;
    oss <<my_rank;
    strcpy(logname, oss.str().c_str());   
    log.open(logname);
#endif    
     

    if (my_rank == 0) {
        cout << "P = " << p << endl;
        initStarter(argc, argv);
    } else
        // rank == 0 
    {
        MPI_Barrier(MPI_COMM_WORLD);
        MPI_Recv(init, 3, MPI_INT, 0, MSG_INIT, MPI_COMM_WORLD, &status);
#ifdef DEBUG
        log << "CPU : " << my_rank << " RECIEVED INIT n = " << init[0] << " s = " << init[1] << " r = " << init[2] << " BOARD : ";
#endif
        //        board = new int[init[0]];
        board = (int *) malloc(init[0] * sizeof (int));
        MPI_Recv(board, init[0], MPI_INT, 0, MSG_INIT_DATA, MPI_COMM_WORLD, &status);
#ifdef DEBUG
        for (int i = 0; i < init[0]; i++) {
            log << board[i] << " ";
        }
        log << endl;
#endif
        n = init[0];
        s = init[1];
        r = init[2];
        game = new Game(n, s, r);
        game->newGame(board);
#ifdef DEBUG

        game->printGame(log);
//        log << "SENDING WORK REQUEST TO 0" << endl;
#endif
//        MPI_Send(&my_rank, 1, MPI_INT, 0, MSG_WORK_REQUEST, MPI_COMM_WORLD);
//        log << "REQUEST SENT...GOODBYE"<<endl;
//        log.close();
//        MPI_Finalize();
//        return 0;
    }




    //
#ifdef DEBUG
    log << "START!!!!" << endl;
#endif
    if (my_rank == 0) {
//    game->printGame(log);
    game->printGame();
    }
    solution = game->play(log, my_rank);
    
    vector<Move> tmp;
    for(int i=1;i<p;i++) {
#ifdef DEBUG
    log << "WAITING FOR SOLUTIONS FROM " << (p-i)<< " CPUs"<< endl;
#endif
        MPI_Recv(init, 3, MPI_INT, MPI_ANY_SOURCE, MSG_SOLUTION, MPI_COMM_WORLD, &status);
#ifdef DEBUG
    log << "    RECIEVING SOLUTION FROM "<< status.MPI_SOURCE << endl;
#endif        
        Comm comm;
        tmp.clear();
        comm.recieve(&tmp,status.MPI_SOURCE,log);
        if (solution.size()==0 || (tmp.size()!=0 && tmp.size()<solution.size())) {
#ifdef DEBUG
    log << "            THIS SOLUTION IS BETTER" << endl;
#endif            
            solution.clear();
            for(int j=0;j<tmp.size();j++) {
                solution.push_back(tmp.at(j));
            }
        }
    }
    if (solution.size()==0) cout << "NO SOLUTION" << endl;
#ifdef DEBUG
    log << solution.size() << endl;
#endif    
    cout << solution.size() << endl;
    for (int i = 0; i < solution.size(); i++) {
        game->printMove(&solution.at(i));
#ifdef DEBUG
        game->printMove(log,&solution.at(i));
#endif        
    }
    game->newGame(board);
    for (int i=0;i<solution.size();i++){
        game->doMove(&solution.at(i));
    }
#ifdef DEBUG   
    game->printGame(log);
#endif    

//        delete[] game;
//            free(game);
//            solution.clear();
    
    MPI_Barrier(MPI_COMM_WORLD);
    t2 = MPI_Wtime();
//    log << "Time : " << (t2-t1) << endl;
    cout << "Time : " << (t2-t1) << endl;

#ifdef DEBUG     
    log.close();
#endif    
    MPI_Finalize();
    return 0;
}
